//
//  GLRender.cpp
//  newAR
//
//  Created by fg on 5/24/16.
//  Copyright © 2016 Shangjin Zhai. All rights reserved.
//

#include "GLRender.hpp"
#include <opencv2/opencv.hpp>



void displayGLUT();

/**
 * @brief Initialises OpenGL.
 *
 * This function initialises the OpenGL state variables.
 */
void initialiseOpenGL()
{
    glClearColor(0.0, 0.0, 0.0, 1.0);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_NORMALIZE);
    
    //glEnable(GL_CULL_FACE);
    //glCullFace(GL_FRONT);
    
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    // Initialise Texture States
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
    
    //glPolygonMode(GL_FRONT, GL_LINE);
    //glPolygonMode(GL_BACK, GL_LINE);
}



/**
 * @brief Initialises GLUT.
 *
 * Creates a window and sets up the necessary state vairables.
 * This function indirectally initialises the OpenGL state variables.
 */
void initialiseGLUT(int width,int height)
{
    int args = 0;
    
    glutInit(&args, 0);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_ALPHA);
    glutInitWindowSize(width, height);
    glutCreateWindow("markerAR");
    
    initialiseOpenGL();
//    
//   glutDisplayFunc(displayGLUT);
//    glutKeyboardFunc(keyboardGLUT);
//    glutMouseFunc(mouseGLUT);
//    glutMotionFunc(mouseMotionGLUT);
}


/**
 * @brief Draws the basic image background image.
 * @param image The image to render.
 */
void renderBackgroundGL(const cv::Mat &img)
{
    // Make sure that the polygon mode is set so we draw the polygons filled
    // (save the state first so we can restore it).
    GLint polygonMode[2];
    glGetIntegerv(GL_POLYGON_MODE, polygonMode);
    glPolygonMode(GL_FRONT, GL_FILL);
    glPolygonMode(GL_BACK, GL_FILL);
    
    // Set up the virtual camera, projecting using simple ortho so we can draw the background image.
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 1.0, 0.0, 1.0);
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    
    // Create a texture (on the first pass only, we will reuse it later) to hold the image we captured.
    static bool textureGenerated = false;
    static GLuint textureId;
    if (!textureGenerated)
    {
        glGenTextures(1, &textureId);
        
        glBindTexture(GL_TEXTURE_2D, textureId);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        
        textureGenerated = true;
    }
    
    // Copy the image to the texture.
    glBindTexture(GL_TEXTURE_2D, textureId);
    
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.size().width, img.size().height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, img.data);
    
    // Draw the image.
    glEnable(GL_TEXTURE_2D);
    glBegin(GL_TRIANGLES);
    glNormal3f(0.0, 0.0, 1.0);
    
    glTexCoord2f(0.0, 1.0);
    glVertex3f(0.0, 0.0, 0.0);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(0.0, 1.0, 0.0);
    glTexCoord2f(1.0, 1.0);
    glVertex3f(1.0, 0.0, 0.0);
    
    glTexCoord2f(1.0, 1.0);
    glVertex3f(1.0, 0.0, 0.0);
    glTexCoord2f(0.0, 0.0);
    glVertex3f(0.0, 1.0, 0.0);
    glTexCoord2f(1.0, 0.0);
    glVertex3f(1.0, 1.0, 0.0);
    glEnd();
    glDisable(GL_TEXTURE_2D);
    
    // Clear the depth buffer so the texture forms the background.
    glClear(GL_DEPTH_BUFFER_BIT);
    
    // Restore the polygon mode state.
    glPolygonMode(GL_FRONT, polygonMode[0]);
    glPolygonMode(GL_BACK, polygonMode[1]);
}



/**
 * @brief Draws a teapot.
 */
void drawStaticTeapotGL(GLfloat* modelMatrix)
{
    // Set the colour for all new objects.
    glColor3f(1.0, 0.0, 0.5);
    
    // The material properties for the teapot.
    float teapotAlpha = 1.0;
    float ambientTeapot[4]  = {1.0, 0.0, 0.0, teapotAlpha};
    float diffuseTeapot[4]  = {1.0, 0.0, 0.0, teapotAlpha};
    float specularTeapot[4] = {1.0, 1.0, 0.0, teapotAlpha};
    float shininessTeapot = 1;
    
    glMaterialfv(GL_FRONT, GL_AMBIENT, ambientTeapot);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseTeapot);
    glMaterialfv(GL_FRONT, GL_SPECULAR, specularTeapot);
    glMaterialf(GL_FRONT, GL_SHININESS, shininessTeapot);
    
    
    // Draw the teapot.
    glPushMatrix();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glLoadMatrixf(modelMatrix);
    glutSolidTeapot(1.0);
    glPopMatrix();
    
}


/**
 * @brief Draws a teapot.
 */
void drawStaticCubeGL(GLfloat* modelMatrix)
{
    // Set the colour for all new objects.
    glColor3f(1.0, 0.0, 0.5);
    
    // The material properties for the teapot.
    float teapotAlpha = 1.0;
    float ambientTeapot[4]  = {1.0, 0.0, 0.0, teapotAlpha};
    float diffuseTeapot[4]  = {1.0, 0.0, 0.0, teapotAlpha};
    float specularTeapot[4] = {1.0, 1.0, 0.0, teapotAlpha};
    float shininessTeapot = 1;
    
    glMaterialfv(GL_FRONT, GL_AMBIENT, ambientTeapot);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, diffuseTeapot);
    glMaterialfv(GL_FRONT, GL_SPECULAR, specularTeapot);
    glMaterialf(GL_FRONT, GL_SHININESS, shininessTeapot);
    
    
    // Draw the teapot.
    glPushMatrix();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glLoadMatrixf(modelMatrix);
    glutSolidCube(1.0);
    glPopMatrix();
    
}
